package udp

import (
	"encoding/xml"
	"fmt"
	"gitee.com/tsingeye/FreeEhome/config"
	"gitee.com/tsingeye/FreeEhome/tools"
	"gitee.com/tsingeye/FreeEhome/tools/logs"
	"gitee.com/tsingeye/FreeEhome/tools/redisDB"
	"gitee.com/tsingeye/FreeEhome/tools/sqlDB"
	"net"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

//UDP网关
type gatewayForUDP struct {
	UDPClients map[string]*udpClient //键是deviceID
	UDPConn    *net.UDPConn
	Sequence   int64 //序列号，用于向设备发送指令，序列号唯一，很关键
	sync.Mutex
}

//创建一个UDP网关
func NewUDPGateway(udpConn *net.UDPConn) *gatewayForUDP {
	return &gatewayForUDP{
		UDPClients: make(map[string]*udpClient),
		UDPConn:    udpConn,
		Sequence:   tools.GetRangeNum(700, 3564),
	}
}

//读取来自UDP客户端的数据
func (gateway *gatewayForUDP) readFromUDP() {
	//延迟关闭监听
	defer gateway.UDPConn.Close()

	for {
		data := make([]byte, 2048)
		//阻塞获取数据
		n, udpAddr, err := gateway.UDPConn.ReadFromUDP(data)
		if err != nil {
			logs.BeeLogger.Error("gatewayForUDP.readFromUDP() ---> udpConn.ReadFromUDP() error:%s", err)
			continue
		}

		//来自EHome的XML协议的数据是GB2312格式的，golang进行处理时需转换为UTF-8格式的
		gb2312Data := data[:n]
		//XML协议格式转换
		utf8Data, err := tools.GB2312ToUTF8(gb2312Data)
		if err != nil {
			logs.BeeLogger.Error("gatewayForUDP.readFromUDP() ---> gb2312Data=%s, tools.GB2312ToUTF8() error:%s", gb2312Data, err)
			continue
		}

		//解析XML数据，判断是何信令
		var xmlBaseMsg xmlBasePacket
		if err := xml.Unmarshal(utf8Data, &xmlBaseMsg); err != nil {
			logs.BeeLogger.Error("gatewayForUDP.readFromUDP() ---> utf8Data=%s, xml.Unmarshal() error:%s", utf8Data, err)
			continue
		}

		switch xmlBaseMsg.CommandType {
		case "REQUEST":
			//接收到来自UDP客户端（EHome设备）请求信令
			switch xmlBaseMsg.Command {
			case "REGISTER":
				//UDP客户端发送注册请求
				logs.BeeLogger.Emergency("gatewayForUDP.readFromUDP() ---> receive udp raw data:%s", gb2312Data)
				go gateway.requestRegister(utf8Data, udpAddr)
			case "KEEPALIVE":
				//UDP客户端发送保活请求
				go gateway.requestKeepalive(utf8Data, udpAddr)
			default:
				logs.BeeLogger.Emergency("gatewayForUDP.readFromUDP() ---> utf8Data=%s, unknown Command=%s!!!", utf8Data, xmlBaseMsg.Command)
			}
		case "RESPONSE":
			//接收到来自UDP客户端（EHome设备）回复信令
			switch xmlBaseMsg.WhichCommand {
			case "GETDEVICECONFIG":
				//设备配置信息回复
				logs.BeeLogger.Emergency("gatewayForUDP.readFromUDP() ---> receive udp raw data:%s", gb2312Data)
				go responseGetDeviceConfig(utf8Data, xmlBaseMsg.Sequence)
			case "GETDEVICEWORKSTATUS":
				//设备工作状态信息回复，存储通道列表
				logs.BeeLogger.Emergency("gatewayForUDP.readFromUDP() ---> receive udp raw data:%s", gb2312Data)
				go responseGetDeviceWorkStatus(utf8Data, xmlBaseMsg.Sequence)
			case "INVITESTREAM":
				//指定通道实时直播启动回复
				logs.BeeLogger.Emergency("gatewayForUDP.readFromUDP() ---> receive udp raw data:%s", gb2312Data)
				go responseInviteStream(utf8Data, xmlBaseMsg.Sequence)
			case "BYESTREAM":
				//关闭实时直播回复
				logs.BeeLogger.Emergency("gatewayForUDP.readFromUDP() ---> receive udp raw data:%s", gb2312Data)
				go responseByeStream(utf8Data, xmlBaseMsg.Sequence)
			case "QUERYRECORDEDFILES":
				//指定通道录像文件查询回复
				logs.BeeLogger.Info("gatewayForUDP.readFromUDP() ---> receive udp raw data:%s", gb2312Data)
				go responseQueryRecordedFiles(utf8Data, xmlBaseMsg.Sequence)
			case "UPLOADFILE":
				//指定通道录像文件播放回复
				logs.BeeLogger.Emergency("gatewayForUDP.readFromUDP() ---> receive udp raw data:%s", gb2312Data)
				go responseUploadFile(utf8Data, xmlBaseMsg.Sequence)
			}
		default:
			//未知协议
			logs.BeeLogger.Emergency("gatewayForUDP.readFromUDP() ---> utf8Data=%s, unknown CommandType=%s!!!", utf8Data, xmlBaseMsg.CommandType)
		}
	}
}

//UDP客户端 ---> UDP服务端：UDP客户端注册请求
func (gateway *gatewayForUDP) requestRegister(utf8Data []byte, udpAddr *net.UDPAddr) {
	var requestData requestRegisterPacket
	if err := xml.Unmarshal(utf8Data, &requestData); err != nil {
		logs.BeeLogger.Error("gatewayForUDP.requestRegister() ---> utf8Data=%s, xml.Unmarshal() error:%s", utf8Data, err)
		return
	}

	fmt.Printf("%s gatewayForUDP.requestRegister() ---> deviceID=%s, deviceIP=%s, request register!!!\n", time.Now().Format(config.TimeLayout), requestData.DeviceID, requestData.LocalIP)
	logs.BeeLogger.Emergency("gatewayForUDP.requestRegister() ---> deviceID=%s, deviceIP=%s, request register!!!", requestData.DeviceID, requestData.LocalIP)

	responseData := responseRegisterPacket{
		XMLName:            xml.Name{},
		Version:            2.5,
		Sequence:           requestData.Sequence,
		CommandType:        "RESPONSE",
		Method:             requestData.Command,
		WhichCommand:       requestData.Command,
		Status:             200,
		Description:        "OK",
		KeepAliveSeconds:   xmlConfig.Param.KeepAliveSeconds,
		AlarmServerIP:      xmlConfig.Param.AlarmServerIP,
		AlarmServerPort:    xmlConfig.Param.AlarmServerUdpPort,
		AlarmServerType:    xmlConfig.Param.AlarmServerType,
		NTPServerIP:        xmlConfig.Param.NTPServerIP,
		NTPServerPort:      xmlConfig.Param.NTPServerPort,
		NTPInterval:        xmlConfig.Param.NTPInterval,
		PictureServer:      xmlConfig.Param.PictureServerIP,
		PictureServerPort:  xmlConfig.Param.PictureServerPort,
		PictureServerType:  xmlConfig.Param.PictureServerType,
		BlackListAddr:      "",
		BlackListName:      "",
		BlackListPort:      0,
		BlackListUser:      "",
		BlackListPassword:  "",
		RegisterServerIP:   "",
		RegisterServerPort: 0,
	}

	gateway.Lock()
	oldClient, ok := gateway.UDPClients[requestData.DeviceID]
	switch ok {
	case true:
		//该设备已经注册过，重复注册，更新UDPAddr
		oldClient.DeviceLocalIP = requestData.LocalIP
		oldClient.UDPAddr = udpAddr
		gateway.UDPClients[requestData.DeviceID] = oldClient
	default:
		//该设备初次注册，存储设备客户端信息
		newClient := &udpClient{
			DeviceID:      requestData.DeviceID,
			DeviceLocalIP: requestData.LocalIP,
			UDPAddr:       udpAddr,
			gateway:       gateway,
		}
		gateway.UDPClients[requestData.DeviceID] = newClient

		//存储设备信息
		go func() {
			//(`deviceID`,`createdAt`,`deviceLocalIP`,`serialNumber`,`status`,`updatedAt`)
			timeNow := time.Now().Format("2006-01-02 15:04:05")
			sqlString := fmt.Sprintf("('%s','%s','%s','%s','%s','%s')", requestData.DeviceID, timeNow, requestData.LocalIP, "", "ON", timeNow)
			if sqlString != "" {
				insertIntoSQL := sqlDB.GetInsertIntoSQL([]string{"deviceID", "createdAt"}, []string{"deviceLocalIP", "serialNumber", "status", "updatedAt"}, sqlDB.GetTableName(&sqlDB.DeviceTable{}), sqlDB.DBType)
				if err := sqlDB.Exec(fmt.Sprintf(insertIntoSQL, sqlString)); err != nil {
					logs.BeeLogger.Emergency("gatewayForUDP.requestRegister() ---> deviceID=%s, deviceIP=%s, sqlDB.GetInsertIntoSQL(&sqlDB.DeviceTable{}) error:%s", requestData.DeviceID, requestData.LocalIP, err)
				}
			}
		}()

		//存入redis数据库中
		if err := redisDB.SetEX(requestData.DeviceID, requestData.DeviceID, xmlConfig.Param.KeepAliveCount*xmlConfig.Param.KeepAliveSeconds); err != nil {
			logs.BeeLogger.Error("gatewayForUDP.requestRegister() ---> deviceID=%s, deviceIP=%s, redisDB.SetEX() key=%s, value=%s, error:%s", requestData.DeviceID, requestData.LocalIP, requestData.DeviceID, requestData.DeviceID, err)
		}

		//开协程检测设备保活
		go gateway.checkKeepalive(requestData.DeviceID)
		//获取GetServerInfo配置信息
		go newClient.requestGetServerInfo()
		//获取GetDeviceWorkStatus状态信息
		go newClient.requestGetDeviceWorkStatus()
	}

	gateway.Unlock()

	//回复注册请求，注意此处不能直接使用上面的oldClient，因为初次注册时上面的oldClient不存在
	gateway.Lock()
	client := gateway.UDPClients[requestData.DeviceID]
	gateway.Unlock()

	switch client.responseToUDPClient(responseData, responseData.WhichCommand, udpAddr) {
	case true:
		//注册成功
		logs.BeeLogger.Emergency("gatewayForUDP.requestRegister() ---> deviceID=%s, deviceIP=%s, register successful!!!", requestData.DeviceID, requestData.LocalIP)
	default:
		//注册失败
		logs.BeeLogger.Emergency("gatewayForUDP.requestRegister() ---> deviceID=%s, deviceIP=%s, register failed!!!", requestData.DeviceID, requestData.LocalIP)
	}
}

//设备保活检测，判断设备是否离线，若离线更新对应设备和通道状态，同时内存删除该设备连接信息
func (gateway *gatewayForUDP) checkKeepalive(deviceID string) {
	idleDelay := time.NewTicker(time.Duration(xmlConfig.Param.KeepAliveSeconds) * time.Second)
	defer idleDelay.Stop()

	for {
		select {
		case <-idleDelay.C:
			if !redisDB.Exists(deviceID) {
				//删除该设备连接信息
				gateway.Lock()
				delete(gateway.UDPClients, deviceID)
				gateway.Unlock()

				//设备离线，重置该设备状态和其推送的通道列表状态
				timeNow := time.Now().Format(config.TimeLayout)
				if err := sqlDB.BatchUpdate(sqlDB.GetTableName(&sqlDB.DeviceTable{}), map[string]interface{}{"status": "OFF", "updatedAt": timeNow}, "deviceID = ?", deviceID); err != nil {
					//更新设备失败，默认是SQL语句错误导致
					logs.BeeLogger.Error("checkKeepalive() ---> deviceID=%s, device offline!!!sqlDB.BatchUpdate(&sqlDB.DeviceTable{}) error:%s", deviceID, err)
				}
				//设备离线，更新该设备推送的所有通道列表的状态
				if err := sqlDB.BatchUpdate(sqlDB.GetTableName(&sqlDB.ChannelTable{}), map[string]interface{}{"status": "OFF", "updatedAt": timeNow}, "deviceID = ?", deviceID); err != nil {
					//更新通道失败，默认是SQL语句错误导致
					logs.BeeLogger.Error("checkKeepalive() ---> deviceID=%s, device offline!!!sqlDB.BatchUpdate(&sqlDB.ChannelTable{}) error:%s", deviceID, err)
				}
				return
			}
		}
	}
}

//UDP客户端 ---> UDP服务端：UDP客户端保活请求
func (gateway *gatewayForUDP) requestKeepalive(utf8Data []byte, udpAddr *net.UDPAddr) {
	var requestData requestKeepalivePacket
	if err := xml.Unmarshal(utf8Data, &requestData); err != nil {
		logs.BeeLogger.Error("gatewayForUDP.requestKeepalive() ---> utf8Data=%s, xml.Unmarshal() error:%s", utf8Data, err)
		return
	}

	responseData := responseKeepalivePacket{
		XMLName:      xml.Name{},
		Version:      2.5,
		Sequence:     requestData.Sequence,
		CommandType:  "RESPONSE",
		Method:       "CONTROL",
		WhichCommand: requestData.Command,
		Status:       200,
		Description:  "OK",
	}

	gateway.Lock()
	client, ok := gateway.UDPClients[requestData.DeviceID]
	gateway.Unlock()

	switch ok {
	case true:
		//设备已注册
		client.responseToUDPClient(responseData, responseData.WhichCommand, udpAddr)

		//重置心跳保活
		redisDB.Expire(requestData.DeviceID, xmlConfig.Param.KeepAliveCount*xmlConfig.Param.KeepAliveSeconds)
	default:
		//设备未注册，此时若收到保活请求回复403让设备发送注册请求
		responseData.Method = "REGISTER"
		responseData.Status = 403
		logs.BeeLogger.Notice("gatewayForUDP.requestKeepalive() ---> deviceID=%s not registered, reply status=403, please register!!!", requestData.DeviceID)

		responseByte, err := xml.MarshalIndent(responseData, " ", "  ")
		if err != nil {
			logs.BeeLogger.Error("gatewayForUDP.requestKeepalive() ---> responseData=%v, xml.MarshalIndent() error:%s", responseData, err)
			return
		}

		gb2312Data, err := tools.UTF8ToGB2312(responseByte)
		if err != nil {
			logs.BeeLogger.Error("gatewayForUDP.requestKeepalive() ---> responseData=%s, tools.UTF8ToGB2312() error:%s", responseByte, err)
			return
		}

		gateway.responseToUDPClient(gb2312Data, udpAddr)
	}
}

//UDP服务端 ---> UDP客户端：向UDP客户端发送回复信令，回复一次，返回bool类型只表示是否将数据发送出去，并不代表UDP客户端一定收到数据
func (gateway *gatewayForUDP) responseToUDPClient(responseData []byte, udpAddr *net.UDPAddr) bool {
	_, err := gateway.UDPConn.WriteToUDP(responseData, udpAddr)
	if err != nil {
		logs.BeeLogger.Error("gatewayForUDP.responseToUDPClient() ---> responseData=%s, UDPConn.WriteToUDP() error:%s", responseData, err)
		return false
	}

	return true
}

//UDP服务端 ---> UDP客户端：向UDP客户端发送请求信令，请求三次，返回bool类型只表示是否将数据发送出去，并不代表UDP客户端一定收到数据
func (gateway *gatewayForUDP) requestToUDPClient(requestData []byte, udpAddr *net.UDPAddr) (retBool bool) {
	for i := 0; i < 3; i++ {
		_, err := gateway.UDPConn.WriteToUDP(requestData, udpAddr)
		if err != nil {
			logs.BeeLogger.Error("gatewayForUDP.requestToUDPClient() ---> requestData=%s, UDPConn.WriteToUDP() error:%s", requestData, err)
		} else {
			retBool = true
		}
	}

	return
}

//UDP客户端
type udpClient struct {
	DeviceID      string //设备ID
	DeviceLocalIP string //设备IP
	UDPAddr       *net.UDPAddr
	//SessionMap    map[string]string //存储实时直播
	sync.Mutex
	gateway *gatewayForUDP
}

//UDP服务端 ---> UDP客户端：向UDP客户端发送回复信令，回复一次。回复时使用从UDP客户端接收到的最新的UDPAddr
func (client *udpClient) responseToUDPClient(responseData interface{}, whichCommand string, udpAddr *net.UDPAddr) bool {
	responseByte, err := xml.MarshalIndent(responseData, " ", "  ")
	if err != nil {
		logs.BeeLogger.Error("udpClient.responseToUDPClient() ---> responseData=%v, xml.MarshalIndent() error:%s", responseData, err)
		return false
	}

	gb2312Data, err := tools.UTF8ToGB2312(responseByte)
	if err != nil {
		logs.BeeLogger.Error("udpClient.responseToUDPClient() ---> responseData=%s, tools.UTF8ToGB2312() error:%s", responseByte, err)
		return false
	}

	switch whichCommand {
	case "KEEPALIVE":
		//回复心跳请求"
		logs.BeeLogger.Debug("udpClient.responseToUDPClient() ---> response to udpClient, gb2312Data=%s", gb2312Data)
	case "REGISTER":
		//回复注册请求
		logs.BeeLogger.Emergency("udpClient.responseToUDPClient() ---> response to udpClient, gb2312Data=%s", gb2312Data)
	default:
		//回复其他信令
		logs.BeeLogger.Info("udpClient.responseToUDPClient() ---> response to udpClient, gb2312Data=%s", gb2312Data)
	}

	return client.gateway.responseToUDPClient(gb2312Data, udpAddr)
}

//UDP服务端 ---> UDP客户端：向UDP客户端发送请求信令，发送三次。主动请求时使用UDP客户端注册时的UDPAddr
func (client *udpClient) requestToUDPClient(requestData interface{}, command string) bool {
	requestByte, err := xml.MarshalIndent(requestData, " ", "  ")
	if err != nil {
		logs.BeeLogger.Error("udpClient.requestToUDPClient() ---> requestData=%v, xml.MarshalIndent() error:%s", requestData, err)
		return false
	}

	gb2312Data, err := tools.UTF8ToGB2312(requestByte)
	if err != nil {
		logs.BeeLogger.Error("udpClient.requestToUDPClient() ---> requestData=%s, tools.UTF8ToGB2312() error:%s", requestByte, err)
		return false
	}

	switch command {
	case "GETDEVICECONFIG", "GETDEVICEWORKSTATUS":
		//请求获取设备配置信令
		//请求获取设备工作状态信令
		logs.BeeLogger.Emergency("udpClient.requestToUDPClient() ---> gb2312Data=%s, request to udpClient!!!", gb2312Data)
	default:
		//请求获取其他操作信令
		logs.BeeLogger.Info("udpClient.requestToUDPClient() ---> gb2312Data=%s, request to udpClient", gb2312Data)
	}

	return client.gateway.requestToUDPClient(gb2312Data, client.UDPAddr)
}

//UDP服务端 ---> UDP客户端：获取设备配置请求，目前主要是获取SerialNumber(设备序列号)
func (client *udpClient) requestGetServerInfo() {
	sequence := atomic.AddInt64(&client.gateway.Sequence, 2)

	requestData := requestGetServerInfoPacket{
		XMLName:      xml.Name{},
		Version:      2.5,
		Sequence:     sequence,
		CommandType:  "REQUEST",
		Method:       "PARAM",
		Command:      "GETDEVICECONFIG",
		ConfigCmd:    "GetServerInfo",
		ConfigParam1: 0,
		ConfigParam2: 0,
		ConfigParam3: 0,
		ConfigParam4: 0,
	}

	//存储键值对。键：序列号(sequence)；值：设备ID(deviceID_GetServerInfo)
	//Command: "GETDEVICECONFIG"下有多种类型，比如此处是"GetServerInfo"，这样存储值的目的是判断收到的数据的具体类型
	//当然可以使用redis存储map或者结构体，但需要进行JSON数据的加解密，麻烦
	if err := redisDB.SetEX(sequence, client.DeviceID+"_GetServerInfo", xmlConfig.Param.KeepAliveCount*xmlConfig.Param.KeepAliveSeconds); err != nil {
		logs.BeeLogger.Error("udpClient.requestGetServerInfo() ---> deviceID=%s, deviceIP=%s, redisDB.SetEX() key=%d, value=%s, error:%s", client.DeviceID, client.DeviceLocalIP, sequence, client.DeviceID+"_GetServerInfo", err)
	}

	switch client.requestToUDPClient(requestData, requestData.Command) {
	case true:
		//发送请求成功
		logs.BeeLogger.Emergency("udpClient.requestGetServerInfo() ---> deviceID=%s, deviceIP=%s, request getServerInfo successful!!!", client.DeviceID, client.DeviceLocalIP)
	default:
		//发送请求失败
		logs.BeeLogger.Emergency("udpClient.requestGetServerInfo() ---> deviceID=%s, deviceIP=%s, request getServerInfo failed!!!", client.DeviceID, client.DeviceLocalIP)
	}
}

//UDP客户端 ---> UDP服务端：获取到设备配置信息回复
func responseGetDeviceConfig(utf8Data []byte, sequence int64) {
	if !redisDB.Exists(sequence) {
		return
	}

	//获取deviceID和请求的配置类型
	deviceIDAndConfig, _ := redisDB.GetString(sequence)

	list := strings.Split(deviceIDAndConfig, "_")
	deviceID := list[0]
	deviceConfig := list[1]

	switch deviceConfig {
	case "GetServerInfo":
		//解析XML数据，获取serialNumber(设备序列号)
		var responseData responseGetServerInfoPacket
		if err := xml.Unmarshal(utf8Data, &responseData); err != nil {
			logs.BeeLogger.Error("getDeviceConfig() ---> utf8Data=%s, xml.Unmarshal() error:%s", utf8Data, err)
			return
		}

		if err := sqlDB.BatchUpdate(sqlDB.GetTableName(&sqlDB.DeviceTable{}), map[string]interface{}{"serialNumber": responseData.SerialNumber, "updatedAt": time.Now().Format(config.TimeLayout)}, "deviceID = ?", deviceID); err != nil {
			//更新设备失败，默认是SQL语句错误导致
			logs.BeeLogger.Error("getDeviceConfig() ---> deviceID=%s, get serialNumber=%s, sqlDB.BatchUpdate(&sqlDB.DeviceTable{}) error:%s", deviceID, err)
		}
	default:
		logs.BeeLogger.Notice("getDeviceConfig() ---> deviceID=%s, deviceConfig=%s, utf8Data=%s, unknown GETDEVICECONFIG!!!", deviceID, deviceConfig, utf8Data)
	}
}

//UDP服务端 ---> UDP客户端： 获取设备工作状态信息请求，主要是用于获取可使用的通道列表
func (client *udpClient) requestGetDeviceWorkStatus() {
	time.Sleep(15 * time.Second)
	sequence := atomic.AddInt64(&client.gateway.Sequence, 2)

	requestData := requestGetDeviceWorkStatusPacket{
		XMLName:     xml.Name{},
		Version:     4.0,
		Sequence:    sequence,
		CommandType: "REQUEST",
		Method:      "QUERY",
		Command:     "GETDEVICEWORKSTATUS",
		Params:      struct{}{},
	}

	//存储键值对。键：序列号(sequence)；值：设备ID(deviceID)
	if err := redisDB.SetEX(sequence, client.DeviceID, xmlConfig.Param.KeepAliveCount*xmlConfig.Param.KeepAliveSeconds); err != nil {
		logs.BeeLogger.Error("udpClient.requestGetDeviceWorkStatus() ---> deviceID=%s, deviceIP=%s, redisDB.SetEX() key=%d, value=%s, error:%s", client.DeviceID, client.DeviceLocalIP, sequence, client.DeviceID, err)
	}

	switch client.requestToUDPClient(requestData, requestData.Command) {
	case true:
		//发送请求成功
		logs.BeeLogger.Emergency("udpClient.requestGetDeviceWorkStatus() ---> deviceID=%s, deviceIP=%s, request getDeviceWorkStatus successful!!!", client.DeviceID, client.DeviceLocalIP)
	default:
		//发送请求失败
		logs.BeeLogger.Emergency("udpClient.requestGetDeviceWorkStatus() ---> deviceID=%s, deviceIP=%s, request getDeviceWorkStatus failed!!!", client.DeviceID, client.DeviceLocalIP)
	}
}

//UDP客户端 ---> UDP服务端：获取设备工作状态信息回复，存储设备通道列表
func responseGetDeviceWorkStatus(utf8Data []byte, sequence int64) {
	if !redisDB.Exists(sequence) {
		return
	}

	//获取deviceID
	deviceID, _ := redisDB.GetString(sequence)

	//解析XML数据，获取通道列表
	var responseData responseGetDeviceWorkStatusPacket
	if err := xml.Unmarshal(utf8Data, &responseData); err != nil {
		logs.BeeLogger.Error("getDeviceWorkStatus() ---> utf8Data=%s, xml.Unmarshal() error:%s", utf8Data, err)
		return
	}

	//方式一：先批量更新该设备推送的旧的通道列表状态，然后执行插入更新。
	//方式二：先删除该设备推送的旧的通道列表，然后执行插入操作
	//使用以上方法的原因是设备重连时所推送的通道列表有可能会变动
	//但是如果考虑到设置通道别名，建议使用方式一
	if err := sqlDB.BatchUpdate(sqlDB.GetTableName(&sqlDB.ChannelTable{}), map[string]interface{}{"status": "OFF", "updatedAt": time.Now().Format(config.TimeLayout)}, "deviceID = ?", deviceID); err != nil {
		//批量更新通道失败，默认是SQL语句错误导致
		logs.BeeLogger.Error("getDeviceWorkStatus() ---> deviceID=%s, get serialNumber=%s, sqlDB.BatchUpdate(&sqlDB.ChannelTable{}) error:%s", deviceID, err)
	}

	sqlString := ""
	timeNow := time.Now().Format(config.TimeLayout)

	for index, ch := range responseData.CHStatus.CHList {
		id := strings.Split(ch, "-")[0]
		channelID := fmt.Sprintf("%s_%s", deviceID, id)
		channelName := fmt.Sprintf("Camera%s", id)

		//(`channelID`,`channelName`,`channelAlias`,`deviceID`,`createdAt`,`status`,`updatedAt`)
		if index == 0 {
			sqlString += fmt.Sprintf("('%s','%s','%s','%s','%s','%s','%s')", channelID, channelName, channelName, deviceID, timeNow, "ON", timeNow)
		} else {
			sqlString += fmt.Sprintf(",('%s','%s','%s','%s','%s','%s','%s')", channelID, channelName, channelName, deviceID, timeNow, "ON", timeNow)
		}
	}

	if sqlString != "" {
		insertIntoSQL := sqlDB.GetInsertIntoSQL([]string{"channelID", "channelName", "channelAlias", "deviceID", "createdAt"}, []string{"status", "updatedAt"}, sqlDB.GetTableName(&sqlDB.ChannelTable{}), sqlDB.DBType)
		if err := sqlDB.Exec(fmt.Sprintf(insertIntoSQL, sqlString)); err != nil {
			logs.BeeLogger.Error("getDeviceWorkStatus() ---> deviceID=%s, sqlDB.GetInsertIntoSQL(&sqlDB.ChannelTable{}) error:%s", deviceID, err)
		}
	}
}

//UDP服务端 ---> UDP客户端： 实时直播启动请求
func (client *udpClient) requestInviteStream(realChannelID int64, channelType string) (replyData map[string]interface{}) {
	sequence := atomic.AddInt64(&client.gateway.Sequence, 2)

	requestData := requestInviteStreamPacket{
		XMLName:     xml.Name{},
		Version:     2.5,
		Sequence:    sequence,
		CommandType: "REQUEST",
		Method:      "MEDIA",
		Command:     "INVITESTREAM",
		Channel:     realChannelID,
		ChannelType: channelType,
		SinkIP:      config.EHomeSinkIP,
		SinkPort:    config.EHomeSinkPort,
	}

	switch client.requestToUDPClient(requestData, requestData.Command) {
	case true:
		//发送请求成功
		logs.BeeLogger.Emergency("udpClient.requestInviteStream() ---> deviceID=%s, deviceIP=%s, request inviteStream successful!!!", client.DeviceID, client.DeviceLocalIP)
	default:
		//发送请求失败
		logs.BeeLogger.Emergency("udpClient.requestInviteStream() ---> deviceID=%s, deviceIP=%s, request inviteStream failed!!!", client.DeviceID, client.DeviceLocalIP)

		replyData = map[string]interface{}{
			"errCode": config.EHomeServerError,
			"errMsg":  config.HTTPCodeMap[config.EHomeServerError],
		}
		return
	}

	//通道ID
	channelID := fmt.Sprintf("%s_%d", client.DeviceID, realChannelID)

	//等待实时直播启动返回的信息，进行相关处理
	idleDelay := time.NewTicker(1 * time.Second)
	defer idleDelay.Stop()
	//超时计数器
	var timeoutCounter int64
	for {
		select {
		case <-idleDelay.C:
			if redisDB.Exists(sequence) {
				utf8Data, _ := redisDB.GetBytes(sequence)
				//删除数据
				redisDB.Del(sequence)

				//解析XML数据，获取实时直播启动成功返回的session
				var responseData responseInviteStreamPacket
				if err := xml.Unmarshal(utf8Data, &responseData); err != nil {
					logs.BeeLogger.Error("udpClient.requestInviteStream() ---> receive utf8Data=%s, xml.Unmarshal() error:%s", utf8Data, err)

					replyData = map[string]interface{}{
						"errCode": config.EHomeServerError,
						"errMsg":  config.HTTPCodeMap[config.EHomeServerError],
					}
					return
				}

				switch responseData.Status {
				case 200:
					logs.BeeLogger.Emergency("udpClient.responseInviteStream() ---> channelID=%s, inviteStream succeed, get session=%s!!!", channelID, responseData.Session)
					fmt.Printf("%s udpClient.responseInviteStream() ---> channelID=%s, inviteStream succeed, get session=%s!!!\n", time.Now().Format(config.TimeLayout), channelID, responseData.Session)

					return waitHookForInviteStream(client.DeviceID, channelID, responseData.Session)
				default:
					//TCP服务器推流失败
					logs.BeeLogger.Emergency("udpClient.responseInviteStream() ---> channelID=%s, inviteStream failed, status=%d!!!", channelID, responseData.Status)
					fmt.Printf("%s udpClient.responseInviteStream() ---> channelID=%s, inviteStream failed, status=%d!!!\n", time.Now().Format(config.TimeLayout), channelID, responseData.Status)

					replyData = map[string]interface{}{
						"errCode": responseData.Status,
						"errMsg":  responseData.Description,
					}
					return
				}
			}

			timeoutCounter += 1
			if timeoutCounter == config.WaitEHomeReplyTime {
				//超时
				logs.BeeLogger.Emergency("udpClient.responseInviteStream() ---> channelID=%s, request inviteStream streamURL timeout!!!", channelID)

				replyData = map[string]interface{}{
					"errCode": config.EHomeUDPReplyTimeout,
					"errMsg":  config.HTTPCodeMap[config.EHomeUDPReplyTimeout],
				}
				return
			}
		}
	}
}

//等待Hook触发on_publish获取session，然后合成流URL
func waitHookForInviteStream(deviceID, channelID, session string) (replyData map[string]interface{}) {
	idleDelay := time.NewTicker(1 * time.Second)
	defer idleDelay.Stop()
	//超时计数器
	var timeoutCounter int64
	for {
		select {
		case <-idleDelay.C:
			if redisDB.Exists(session) {
				//存储session用于关闭实时直播流
				redisDB.SetEX(session, deviceID, 7*24*3600)

				replyData = map[string]interface{}{
					"errCode": config.EHomeSuccessOK,
					"errMsg":  config.HTTPCodeMap[config.EHomeSuccessOK],
					"urlMap": map[string]string{
						"rtmp": tools.StringsJoin("rtmp://", config.StreamIP, ":", config.RTMPPort, "/live/", session),
						"flv":  tools.StringsJoin("http://", config.StreamIP, ":", config.HLSPort, "/live/", session, ".flv"),
						"rtsp": tools.StringsJoin("rtsp://", config.StreamIP, ":", config.RTSPPort, "/live/", session),
						"hls":  tools.StringsJoin("http://", config.StreamIP, ":", config.HLSPort, "/live/", session, "/hls.m3u8"),
					},
				}
				return
			}

			timeoutCounter += 1
			if timeoutCounter == config.WaitHookOnPublishTimeout {
				//超时
				logs.BeeLogger.Emergency("waitHookReplySession() ---> channelID=%s, response inviteStream timeout!!!", channelID)

				replyData = map[string]interface{}{
					"errCode": config.EHomeHookReplySessionTimeout,
					"errMsg":  config.HTTPCodeMap[config.EHomeHookReplySessionTimeout],
				}
				return
			}
		}
	}

	return
}

//UDP客户端 ---> UDP服务端：实时直播启动回复，status=200时返回session，status=401表示TCP推流失败
func responseInviteStream(utf8Data []byte, sequence int64) {
	//存储获得的实时直播启动回复信息
	if err := redisDB.SetEX(sequence, utf8Data, config.WaitEHomeReplyTime*2); err != nil {
		logs.BeeLogger.Error("responseInviteStream() ---> key=%d, value=%s, redisDB.SetEX() error:%s", sequence, utf8Data, err)
	}
}

//UDP服务端 ---> UDP客户端： 实时直播关闭请求
func (client *udpClient) requestByeStream(session string) (replyData map[string]interface{}) {
	sequence := atomic.AddInt64(&client.gateway.Sequence, 2)

	requestData := requestByeStreamPacket{
		XMLName:     xml.Name{},
		Version:     2.5,
		Sequence:    sequence,
		CommandType: "REQUEST",
		Method:      "MEDIA",
		Command:     "BYESTREAM",
		Session:     session,
	}

	switch client.requestToUDPClient(requestData, requestData.Command) {
	case true:
		//发送请求成功
		logs.BeeLogger.Emergency("udpClient.requestByeStream() ---> deviceID=%s, deviceIP=%s, request byeStream successful!!!", client.DeviceID, client.DeviceLocalIP)
	default:
		//发送请求失败
		logs.BeeLogger.Emergency("udpClient.requestByeStream() ---> deviceID=%s, deviceIP=%s, request byeStream failed!!!", client.DeviceID, client.DeviceLocalIP)

		replyData = map[string]interface{}{
			"errCode": config.EHomeServerError,
			"errMsg":  config.HTTPCodeMap[config.EHomeServerError],
		}
		return
	}

	replyData = map[string]interface{}{
		"errCode": config.EHomeSuccessOK,
		"errMsg":  config.HTTPCodeMap[config.EHomeSuccessOK],
	}
	return
}

//UDP客户端 ---> UDP服务端：实时直播关闭回复
func responseByeStream(utf8Data []byte, sequence int64) {
	var responseData responseByeStreamPacket
	if err := xml.Unmarshal(utf8Data, &responseData); err != nil {
		logs.BeeLogger.Error("responseByeStream() ---> utf8Data=%s, xml.Unmarshal() error:%s", utf8Data, err)
		return
	}

	switch responseData.Status {
	case 200:
		//实时直播关闭成功
		logs.BeeLogger.Emergency("responseByeStream() ---> session=%d, byeStream successful!!!", responseData.Session)
		fmt.Printf("responseByeStream() ---> session=%s, byeStream successful!!!\n", responseData.Session)
	default:
		//实时直播关闭失败
		logs.BeeLogger.Emergency("responseByeStream() ---> session=%d, byeStream failed!!!", responseData.Session)
		fmt.Printf("responseByeStream() ---> session=%s, byeStream failed!!!\n", responseData.Session)
	}
}

//UDP服务端 ---> UDP客户端： 查询指定通道录像文件请求
func (client *udpClient) requestQueryRecordedFiles(realChannelID, fileType int64, startTime, stopTime string) (replyData map[string]interface{}) {
	sequence := atomic.AddInt64(&client.gateway.Sequence, 2)

	requestData := requestQueryRecordedFilesPacket{
		XMLName:     xml.Name{},
		Version:     2.5,
		Sequence:    sequence,
		CommandType: "REQUEST",
		Method:      "QUERY",
		Command:     "QUERYRECORDEDFILES",
		Channel:     realChannelID,
		FileType:    fileType,
		StartTime:   startTime,
		StopTime:    stopTime,
		ChunkSize:   8,
		StartIdx:    0,
	}

	switch client.requestToUDPClient(requestData, requestData.Command) {
	case true:
		//发送请求成功
		logs.BeeLogger.Emergency("udpClient.requestQueryRecordedFiles() ---> deviceID=%s, deviceIP=%s, request queryRecordedFiles successful!!!", client.DeviceID, client.DeviceLocalIP)
	default:
		//发送请求失败
		logs.BeeLogger.Emergency("udpClient.requestQueryRecordedFiles() ---> deviceID=%s, deviceIP=%s, request queryRecordedFiles failed!!!", client.DeviceID, client.DeviceLocalIP)

		replyData = map[string]interface{}{
			"errCode": config.EHomeServerError,
			"errMsg":  config.HTTPCodeMap[config.EHomeServerError],
		}
		return
	}

	//通道ID
	channelID := fmt.Sprintf("%s_%d", client.DeviceID, realChannelID)

	//等待查询录像文件返回的信息，进行相关处理
	idleDelay := time.NewTicker(100 * time.Millisecond)
	defer idleDelay.Stop()
	//超时计数器
	var timeoutCounter int64
	for {
		select {
		case <-idleDelay.C:
			if redisDB.Exists(sequence) {
				utf8Data, _ := redisDB.GetBytes(sequence)
				//删除数据
				redisDB.Del(sequence)

				//解析XML数据，获取实时直播启动成功返回的session
				var responseData responseQueryRecordedFilesPacket
				if err := xml.Unmarshal(utf8Data, &responseData); err != nil {
					logs.BeeLogger.Error("udpClient.requestQueryRecordedFiles() ---> receive utf8Data=%s, xml.Unmarshal() error:%s", utf8Data, err)

					replyData = map[string]interface{}{
						"errCode": config.EHomeServerError,
						"errMsg":  config.HTTPCodeMap[config.EHomeServerError],
					}
					return
				}

				switch responseData.Status {
				case 200:
					logs.BeeLogger.Info("udpClient.requestQueryRecordedFiles() ---> channelID=%s, response queryRecordedFiles successful", channelID)

					recordedFiles := make([]map[string]interface{}, 0)
					for _, value := range responseData.RecordedFiles {
						list := strings.Split(value, " ")
						if len(list) >= 7 {
							fileSize, err := strconv.ParseInt(list[5], 0, 64)
							if err != nil {
								logs.BeeLogger.Error("udpClient.requestQueryRecordedFiles() ---> channelID=%s, parse fileSize error:%s", err)
								continue
							}
							recordedFiles = append(recordedFiles, map[string]interface{}{
								"filename":  list[0],
								"fileSize":  fileSize,
								"startTime": fmt.Sprintf("%s %s", list[1], list[2]),
								"stopTime":  fmt.Sprintf("%s %s", list[3], list[4]),
							})
						}
					}

					replyData = map[string]interface{}{
						"errCode":       config.EHomeSuccessOK,
						"errMsg":        config.HTTPCodeMap[config.EHomeSuccessOK],
						"recordedFiles": recordedFiles,
					}
					return
				default:
					logs.BeeLogger.Notice("udpClient.requestQueryRecordedFiles() ---> channelID=%s, response queryRecordedFiles failed, status=%d", channelID, responseData.Status)

					replyData = map[string]interface{}{
						"errCode": responseData.Status,
						"errMsg":  responseData.Description,
					}
					return
				}
			}

			timeoutCounter += 1
			if timeoutCounter*10 == config.WaitEHomeReplyTime {
				//超时
				logs.BeeLogger.Emergency("udpClient.requestQueryRecordedFiles() ---> channelID=%s, response queryRecordedFiles timeout!!!", channelID)

				replyData = map[string]interface{}{
					"errCode": config.EHomeUDPReplyTimeout,
					"errMsg":  config.HTTPCodeMap[config.EHomeUDPReplyTimeout],
				}
				return
			}
		}
	}

	return
}

//UDP客户端 ---> UDP服务端：查询设备录像文件回复
func responseQueryRecordedFiles(utf8Data []byte, sequence int64) {
	if err := redisDB.SetEX(sequence, utf8Data, config.WaitEHomeReplyTime*2); err != nil {
		logs.BeeLogger.Error("responseQueryRecordedFiles() ---> key=%d, value=%s, redisDB.SetEX() error:%s", sequence, utf8Data, err)
	}
}

//UDP服务端 ---> UDP客户端： 指定通道录像文件播放
func (client *udpClient) requestUploadFile(realChannelID int64, filename string) (replyData map[string]interface{}) {
	sequence := atomic.AddInt64(&client.gateway.Sequence, 2)

	requestData := requestUploadFilePacket{
		XMLName:      xml.Name{},
		Version:      2.5,
		Sequence:     sequence,
		CommandType:  "REQUEST",
		Method:       "MEDIA",
		Command:      "UPLOADFILE",
		Chan:         realChannelID,
		Filename:     filename,
		SeekType:     1,
		Seek:         0,
		SeekSpan:     0,
		UpServerIP:   config.EHomeUpServerIP,
		UpServerPort: config.EHomeUpServerPort,
	}

	switch client.requestToUDPClient(requestData, requestData.Command) {
	case true:
		//发送请求成功
		logs.BeeLogger.Emergency("udpClient.requestUploadFile() ---> deviceID=%s, deviceIP=%s, filename=%s, request uploadFile successful!!!", client.DeviceID, client.DeviceLocalIP, filename)
	default:
		//发送请求失败
		logs.BeeLogger.Emergency("udpClient.requestUploadFile() ---> deviceID=%s, deviceIP=%s, filename=%s, request uploadFile failed!!!", client.DeviceID, client.DeviceLocalIP, filename)

		replyData = map[string]interface{}{
			"errCode": config.EHomeServerError,
			"errMsg":  config.HTTPCodeMap[config.EHomeServerError],
		}
		return
	}

	//通道ID
	channelID := fmt.Sprintf("%s_%d", client.DeviceID, realChannelID)

	//等待指定通道录像文件播放的返回信息，进行相关处理
	idleDelay := time.NewTicker(1 * time.Second)
	defer idleDelay.Stop()
	//超时计数器
	var timeoutCounter int64
	for {
		select {
		case <-idleDelay.C:
			if redisDB.Exists(sequence) {
				utf8Data, _ := redisDB.GetBytes(sequence)
				//删除数据
				redisDB.Del(sequence)

				//解析XML数据，获取实时直播启动成功返回的session
				var responseData responseUploadFilePacket
				if err := xml.Unmarshal(utf8Data, &responseData); err != nil {
					logs.BeeLogger.Error("udpClient.requestUploadFile() ---> receive utf8Data=%s, xml.Unmarshal() error:%s", utf8Data, err)

					replyData = map[string]interface{}{
						"errCode": config.EHomeServerError,
						"errMsg":  config.HTTPCodeMap[config.EHomeServerError],
					}
					return
				}

				switch responseData.Status {
				case 200:
					logs.BeeLogger.Info("udpClient.requestUploadFile() ---> channelID=%s, filename=%s, response uploadFile successful", channelID, filename)

					return waitHookForUploadFile(channelID)
				case 401:
					logs.BeeLogger.Info("udpClient.requestUploadFile() ---> channelID=%s, filename=%s, response uploadFile successful", channelID, filename)

					return waitHookForUploadFile(channelID)
				default:
					logs.BeeLogger.Notice("udpClient.requestUploadFile() ---> channelID=%s, filename=%s, response uploadFile failed, status=%d", channelID, filename, responseData.Status)

					replyData = map[string]interface{}{
						"errCode": responseData.Status,
						"errMsg":  responseData.Description,
					}
					return
				}
			}

			timeoutCounter += 1
			if timeoutCounter == config.WaitEHomeReplyTime {
				//超时
				logs.BeeLogger.Emergency("udpClient.requestUploadFile() ---> channelID=%s, response uploadFile timeout!!!", channelID)

				replyData = map[string]interface{}{
					"errCode": config.EHomeUDPReplyTimeout,
					"errMsg":  config.HTTPCodeMap[config.EHomeUDPReplyTimeout],
				}
				return
			}
		}
	}

	return
}

//等待Hook触发on_record_mp4获取url，然后返回合成的录像回放url
func waitHookForUploadFile(channelID string) (replyData map[string]interface{}) {
	idleDelay := time.NewTicker(1 * time.Second)
	defer idleDelay.Stop()
	//超时计数器
	var timeoutCounter int64
	for {
		select {
		case <-idleDelay.C:
			if redisDB.Exists("ehrecord") {
				url, _ := redisDB.GetString("ehrecord")
				redisDB.Del("ehrecord")

				switch url {
				case "":
					replyData = map[string]interface{}{
						"errCode": config.EHomeOnRecordMP4HookReplyError,
						"errMsg":  config.HTTPCodeMap[config.EHomeOnRecordMP4HookReplyError],
					}
					return
				default:
					replyData = map[string]interface{}{
						"errCode": config.EHomeSuccessOK,
						"errMsg":  config.HTTPCodeMap[config.EHomeSuccessOK],
						"urlMap": map[string]string{
							"rtmp": tools.StringsJoin("rtmp://", config.StreamIP, ":", config.RTMPPort, "/ehrecord/", url),
							"flv":  tools.StringsJoin("http://", config.StreamIP, ":", config.HLSPort, "/ehrecord/", url, ".flv"),
							"rtsp": tools.StringsJoin("rtsp://", config.StreamIP, ":", config.RTSPPort, "/ehrecord/", url),
							"hls":  tools.StringsJoin("http://", config.StreamIP, ":", config.HLSPort, "/ehrecord/", url, "/hls.m3u8"),
						},
					}
					return
				}
			}

			timeoutCounter += 1
			if timeoutCounter == config.WaitHookOnRecordMP4Timeout {
				//超时
				logs.BeeLogger.Emergency("waitHookForUploadFile() ---> channelID=%s, request uploadFile streamURL timeout!!!", channelID)

				replyData = map[string]interface{}{
					"errCode": config.EHomeHookReplySessionTimeout,
					"errMsg":  config.HTTPCodeMap[config.EHomeHookReplySessionTimeout],
				}
				return
			}
		}
	}

	return
}

//UDP客户端 ---> UDP服务端：指定通道录像文件播放回复
func responseUploadFile(utf8Data []byte, sequence int64) {
	if err := redisDB.SetEX(sequence, utf8Data, config.WaitEHomeReplyTime*2); err != nil {
		logs.BeeLogger.Error("responseUploadFile() ---> key=%d, value=%s, redisDB.SetEX() error:%s", sequence, utf8Data, err)
	}
}

//UDP服务端 ---> UDP客户端： 云台控制请求
func (client *udpClient) ptzControl(realChannelID int64, ptzCmd, action string, speed int64) (replyData map[string]interface{}) {
	sequence := atomic.AddInt64(&client.gateway.Sequence, 2)

	requestData := ptzControlPacket{
		XMLName:     xml.Name{},
		Version:     2.5,
		Sequence:    sequence,
		CommandType: "REQUEST",
		Method:      "CONTROL",
		Command:     "PTZCONTROL",
		Channel:     realChannelID,
		PTZCmd:      ptzCmd,
		Action:      action,
		Speed:       speed,
	}
	switch client.requestToUDPClient(requestData, requestData.Command) {
	case true:
		//发送请求成功
		logs.BeeLogger.Emergency("udpClient.requestQueryRecordedFiles() ---> deviceID=%s, deviceIP=%s, request queryRecordedFiles successful!!!", client.DeviceID, client.DeviceLocalIP)

		replyData = map[string]interface{}{
			"errCode": config.EHomeSuccessOK,
			"errMsg":  config.HTTPCodeMap[config.EHomeSuccessOK],
		}

	default:
		//发送请求失败
		logs.BeeLogger.Emergency("udpClient.requestQueryRecordedFiles() ---> deviceID=%s, deviceIP=%s, request queryRecordedFiles failed!!!", client.DeviceID, client.DeviceLocalIP)

		replyData = map[string]interface{}{
			"errCode": config.EHomeServerError,
			"errMsg":  config.HTTPCodeMap[config.EHomeServerError],
		}
		return
	}

	return
}
